package org.activequant.util.ib.marketscanners;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.activequant.core.domainmodel.marketscanner.MarketScan;
import org.activequant.core.domainmodel.marketscanner.MarketScanChange;
import org.activequant.core.domainmodel.marketscanner.ScanCriteria;
import org.activequant.core.types.TimeFrame;
import org.activequant.data.annotations.Vendor;
import org.activequant.data.retrieval.IMarketScanChangeSubscriptionSource;
import org.activequant.data.retrieval.IMarketScanSubscription;
import org.activequant.data.retrieval.ScanSubscriptionSourceBase;
import org.activequant.util.ib.IBTwsConnection;
import org.activequant.util.pattern.events.IEventListener;
import org.apache.log4j.Logger;

/**
 * @Vendor("IB")<br/>
 * IBMarketScanChangeSource extends MarketScanSubscriptionSourceBase&lt;MarketScanChange&gt; implements IMarketScanChangeSubscriptionSource<br/>
 * Holds the following associated variables:
 * <ul>
 * <li>connection(IBTwsConnection)</li>
 * <li>previousScans(Map&lt;ScanCriteria,MarketScan&gt;)</li>
 * </ul>
 * Holds the following inherited variables:
 * <ul>
 * <li>activeBackends(Map&lt;Topic,Subscription&gt;)</li>
 * <li>activeSubscriptions(Queue&lt;SubscriptionDelegate&gt;)</li>
 * </ul>
 * @author Dimitar
 *
 */
@Vendor("IB")
public class IBMarketScanChangeSource extends ScanSubscriptionSourceBase<MarketScanChange> implements IMarketScanChangeSubscriptionSource {
	
	protected final static Logger log = Logger.getLogger(IBMarketScanChangeSource.class);
	/**
	 * private IBTwsConnection connection;
	 */
	private IBTwsConnection connection;
	/**
	 * static Map&lt;ScanCriteria,MarketScan&gt; previousScans=new ConcurrentHashMap&lt;ScanCriteria,MarketScan&gt;();
	 */
	static Map<ScanCriteria,MarketScan> previousScans=new ConcurrentHashMap<ScanCriteria,MarketScan>();
	/**
	 * constructs a IBMarketScanSource(extends MarketScanSubscriptionSourceBase&lt;MarketScan&gt; implements IMarketScanSubscriptionSource) using the given monitor(IBTwsConnection)
	 * to set its associated connection(IBTwsConnection)
	 * @param monitor
	 */
	public IBMarketScanChangeSource(IBTwsConnection monitor) {
		this.connection = monitor;
	}
	/**
	 * MarketScanSubscription extends Subscription. Holds the following associated variables:
	 * <ul>
	 * <li>spec(ScanCriteria)</li>
	 * <li>listener(IEventListener&lt;MarketScan&gt;)</li>
	 * <li>listener(IEventListener&lt;MarketScan&gt;)</li>
	 * </ul>
	 * Holds the following inherited variables:
	 * <ul>
	 * <li>isActive(AtomicBoolean)</li>
	 * <li>refCount(AtomicInteger)</li>
	 * <li>instrument(InstrumentSpecification)</li>
	 * <li>event(Event&lt;MarketScan&gt;)</li>
	 * </ul>
	 * @author Dimitar
	 *
	 */
	private class MarketScanChangeSubscription extends Subscription {
		/**
		 * private final ScanCriteria spec;
		 */
		private ScanCriteria spec=null;
		/**
		 * private final IEventListener&lt;MarketScan&gt; listener = new IEventListener&lt;MarketScan&gt;() {...}<br/>
		 * A listener that re-fires any event(MarketScanChange)
		 */
		private final IEventListener<MarketScanChange> changeListener = new IEventListener<MarketScanChange>() {
			public void eventFired(MarketScanChange event) {
				fireEvent(event);
			}
		};
		/**
		 * private MarketScanChangeConvertor convertor=new MarketScanChangeConvertor(changeListener);
		 */
		private MarketScanChangeConvertor convertor=new MarketScanChangeConvertor(changeListener);
		/**
		 * constructs a MarketScanSubscription(extends Subscription) using the given s(ScanCriteria) to set its associated spec(ScanCriteria)
		 * @param s
		 */
		MarketScanChangeSubscription(ScanCriteria s) {
			spec = s;
		}
		/**
		 * private final IEventListener&lt;MarketScan&gt; listener = new IEventListener&lt;MarketScan&gt;() {...}<br/>
		 * A listener that detects changes with every MarketScan and re-fires appropriate MarketScanChange events on the convertor 
		 */
		private final IEventListener<MarketScan> listener = new IEventListener<MarketScan>() {
			
			public void eventFired(MarketScan event) {
				 MarketScan previousScan=previousScans.get(spec);
				if(previousScan!=null){
					convertor.processNewMarketScan(previousScan, event);
				}
				//fireEvent(event);
				previousScans.put(spec, event);				
			}
		};		
		/**
		 * Subscribes the associated listener(IEventListener&lt;MarketScan&gt;) for real time bars from the connection(IBTwsConnection) for the associated spec(ScanCriteria)
		 */
		@Override
		protected void handleActivate() {
			connection.subscribeToMarketScanStream(listener, spec);
		}
		/**
		 * Unsubscribes the associated listener(IEventListener&lt;MarketScan&gt;) from listening to real time bars from the connection(IBTwsConnection) for the associated spec(ScanCriteria)
		 */
		@Override
		protected void handleCancel() {
			connection.unsubscribeFromMarketScanStream(listener, spec);
		}
	}
	
	/**
	 * Creates and returns a MarketScanSubscription(extends Subscription) using the given spec(ScanCriteria) to set its associated spec(ScanCriteria)
	 */
	@Override
	protected Subscription createSubscription(ScanCriteria spec, TimeFrame timeFrame) {
		return new MarketScanChangeSubscription(spec);
	}

	public IMarketScanSubscription<MarketScanChange> subscribe(ScanCriteria spec) throws Exception {
		return subscribe(spec, TimeFrame.TIMEFRAME_1_TICK);
	}
	
	/**
	 * we have to do this since IB allows up to 10 MarketScanners (doesn't matter if we cancel them, once you reach 10 you are done)
	 */
	public void reconnect() {
		connection.reconnect();
	}

}